package com.darsystems.foghourz.client;

import java.util.ArrayList;
import java.util.Date;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.googlecode.gchart.client.GChart;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Foghourz implements EntryPoint {
	
	
	private static final NumberFormat hoursFormat = NumberFormat.getFormat("0.0");
	private static final NumberFormat percentFormat = NumberFormat.getFormat("0%");
	/**
	 * Create a remote service proxy to talk to the server-side Fog Bugz service.
	 */
	private final FogBugzServiceAsync fogbugzService = GWT
			.create(FogBugzService.class);

	private VerticalPanel mainPanel = null;
	private Panel dateRangePanel;
	private Panel workingOnPanel;
	private Label workingOn;
	private StatusDisplay statusDisplay;
	
	private FBAuthorizer fbAuthorizer = new FBAuthorizer();
	
	private ArrayList<RadioButton> previousDays = new ArrayList<RadioButton>();
	
	final private DateTimeFormat workDateFormat = DateTimeFormat.getFormat("MM-dd");
	private FlexTable dailyWorkTable = new FlexTable();
	private GChartEx workSummaryChart = new GChartEx();
	
	static final private boolean SHOW_CHART = false;
	
	private UserWorkInfo _workInfo;
	
	static { 
	    GChart.setCanvasFactory(new GChartCanvasFactory());
	  } 
	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
				
		ArrayList<Authorizer> authorizers = new ArrayList<Authorizer>();
		authorizers.add(new GoogleAuthorizer());
		authorizers.add(fbAuthorizer);
		
		StatusPanel status = new StatusPanel("Starting...", authorizers, new LoginHandler()
		{
			public void loginFailed() {
			}
			public void loginSucceeded() {
				startGetUserInfo();
			}
		});
		statusDisplay = status;
		
		RootPanel.get("statusPanelDiv").add(status);
		status.getAuthorized();
	}

	private void buildMainPanel()
	{
		if(mainPanel != null)
		{
			return;
		}
		RootPanel.get("mainPanelDiv").add(buildInitialPanel());
	}
	
	private void startGetUserInfo()
	{
		statusDisplay.setCommMessage("Getting work hours...");
		fogbugzService.getUserWorkInfo(getToken(), new AsyncCallback<UserWorkInfo>(){
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				statusDisplay.problem("SERVER ERROR: " + caught.getMessage());
			}

			public void onSuccess(UserWorkInfo result) {
				if(result == null)
				{
					statusDisplay.problem("No work summaries");
				}
				else
				{
					_workInfo = result;
					statusDisplay.everythingOK("");
					finishStartup();
				}
			}
		});
	}

	private void finishStartup()
	{
		statusDisplay.setCommMessage("Getting user work info");
		buildMainPanel();
		
		startRefreshWorkSummary();
	}

	private int getPreviousDays()
	{
		for(RadioButton option : previousDays)
		{
			if(option.getValue())
			{
				return Integer.parseInt(option.getText());
			}
		}
		
		return 120;
	}
	
	private void addPreviousDaysOption(Integer days)
	{
		RadioButton option = new RadioButton("previousDaysBtn", days.toString());
		previousDays.add(option);
		option.addClickHandler(new ClickHandler()
		{
			public void onClick(ClickEvent event) {
				startRefreshWorkSummary();
			}
			
		});
	}
	
	private Panel buildInitialPanel() {
		
		mainPanel = new VerticalPanel(); 
		dateRangePanel = createWorkDateRangePanel();

		workingOnPanel = createButtonPanel();
		
		mainPanel.add(workingOnPanel);
		mainPanel.add(dateRangePanel);
		mainPanel.add(dailyWorkTable);
		mainPanel.add(workSummaryChart);
		
		setupDailyWorkTable();
		return mainPanel;
	}
	
	private Panel createButtonPanel() {
		final HorizontalPanel panel = new HorizontalPanel();
		
		panel.setSpacing(20);
		final Image stopImg = new Image("img/stopWork.png");
		stopImg.setPixelSize(32, 32);
		final PushButton stopWorkButton = new PushButton(stopImg);
		stopWorkButton.setTitle("Stop Work");
		
		stopWorkButton.addClickHandler(new ClickHandler() {
	    	public void onClick(ClickEvent event) {
	    		stopWorking();	    		
	    	}
	    });

		workingOn = new Label();
		
		panel.add(stopWorkButton);
		panel.add(workingOn);
		
		return panel;
	}

	private Panel createWorkDateRangePanel()
	{
    	final Label title = new Label("Work Summary");
		
		final FlowPanel optionsPanel = new FlowPanel();
		
		addPreviousDaysOption(7);
		addPreviousDaysOption(30);
		addPreviousDaysOption(90);
		addPreviousDaysOption(180);
		addPreviousDaysOption(365);
		previousDays.get(0).setValue(true);
		for(RadioButton option : previousDays)
		{
			optionsPanel.add(option);
		}

		final VerticalPanel leftPanel = new VerticalPanel();
		leftPanel.add(title);
		leftPanel.add(optionsPanel);
		
		return leftPanel;
	}
	
	private void clearTable()
	{
		//TODO - gotta be a better way than this!
		while(dailyWorkTable.getRowCount() > 1)
		{
			dailyWorkTable.removeRow(1);
		}
	}
	
	private String getToken()
	{
		return fbAuthorizer.getToken();
	}
	
	private void stopWorking()
	{
		fogbugzService.stopWork(getToken(), new AsyncCallback<FBResponse>(){
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				statusDisplay.problem("SERVER ERROR: " + caught.getMessage());
			}

			public void onSuccess(FBResponse result) {
				if(!result.isOK())
				{
					statusDisplay.problem("Error stopping work.");
				}
				else
				{
					startRefreshWorkSummary();
				}
			}
		});
		statusDisplay.setCommMessage("Stopping work...");
	}
	
	private void startRefreshWorkSummary()
	{
		Date endDate = new Date();
		
		fogbugzService.getDailyWork(getToken(), endDate, getPreviousDays(), new AsyncCallback<DailyWork[]>(){
			public void onFailure(Throwable caught) {
				// Show the RPC error message to the user
				statusDisplay.problem("SERVER ERROR: " + caught.getMessage());
			}

			public void onSuccess(DailyWork[] result) {
				if(result == null)
				{
					statusDisplay.problem("No work summaries");
				}
				else
				{
					fillSummaryTable(result);
					statusDisplay.everythingOK("");
				}
			}
		});

		if(SHOW_CHART)
		{
			workSummaryChart.setVisible(false);
		}
		clearTable();
		statusDisplay.setCommMessage("Getting work summaries...");
	}

	private double getWorkingHours()
	{
		return _workInfo == null ? 9.0 : _workInfo.getNumberWorkHours();
	}
	
	private double getFBPercentage()
	{
		return _workInfo == null ? .8 : _workInfo.getPercentFBTime()/100.0;
	}
	
	private void fillSummaryTable(DailyWork [] workSummaries)
	{	
		double totalHours = 0.0;
		String workingOnBug = null;
		
		final double targetPercent = getFBPercentage();
		final double workingHours = getWorkingHours();
		
		for(int i = 0;i < workSummaries.length;++i)
		{
			DailyWork dw = workSummaries[i];
			
			dailyWorkTable.setText(i+1, 0, workDateFormat.format(dw.getWorkDate()));
			
			double hoursWorked = dw.getHoursWorked();
			totalHours += hoursWorked;
			
			dailyWorkTable.setText(i+1, 1, hoursFormat.format(hoursWorked));
			double percent = hoursWorked/workingHours;
			dailyWorkTable.setText(i+1, 2, percentFormat.format(percent));
			dailyWorkTable.setWidget(i+1, 3, DailySparkLine.create(percent, targetPercent));
			setDailyWorkTableRowStyle(i+1);
			
			if(workingOnBug == null)
			{
				workingOnBug = dw.getWorkingOn();
			}
		}
		
		setWorkingOnLabel(workingOnBug);
		
		if(workSummaries.length == 0)
		{
			return;
		}
		
		int lastRow = workSummaries.length + 2;
		totalHours /= workSummaries.length;
		
		addAverageDisplayRow(lastRow, totalHours, targetPercent);
		
		if(SHOW_CHART)
		{
			workSummaryChart.setVisible(true);
			workSummaryChart.fill(workSummaries);
			workSummaryChart.update();
		}
	}

	private void setWorkingOnLabel(String workingOnBug)
	{
		workingOn.setText("Working on " + 
				((workingOnBug == null || workingOnBug.length() == 0) ? "nothing" : workingOnBug));
	}
	
	private void addAverageDisplayRow(int lastRow, double averageHours, double targetPercentage) {
		
		dailyWorkTable.setText(lastRow, 0, "Ave:");
		dailyWorkTable.setText(lastRow, 1, hoursFormat.format(averageHours));
		double percent = averageHours/getWorkingHours();
		dailyWorkTable.setText(lastRow, 2, percentFormat.format(percent));
		dailyWorkTable.setWidget(lastRow, 3, DailySparkLine.create(percent, targetPercentage));

		dailyWorkTable.getRowFormatter().addStyleName(lastRow, "workSummaryAverages");
		setDailyWorkTableRowStyle(lastRow+1);

	}

	private void setDailyWorkTableRowStyle(int row) {
		dailyWorkTable.getCellFormatter().addStyleName(row, 1, "workSummaryListNumericColumn");
		dailyWorkTable.getCellFormatter().addStyleName(row, 2, "workSummaryListNumericColumn");
		if(row == 0)
		{
			dailyWorkTable.getRowFormatter().addStyleName(row, "workSummaryListHeader");
		}
		else if((row % 2) == 0)
		{
			dailyWorkTable.getRowFormatter().addStyleName(row, "altRow");
		}
	}
	
	private void setupDailyWorkTable()
	{
		dailyWorkTable.setCellSpacing(0);
		
		dailyWorkTable.setText(0, 0, "Date");
		dailyWorkTable.setText(0, 1, "Hours");
		final double targetPercent = getFBPercentage();
		dailyWorkTable.setText(0, 2, percentFormat.format(targetPercent));
		dailyWorkTable.setWidget(0, 3, DailySparkLine.create(targetPercent, targetPercent));
		
		dailyWorkTable.addStyleName("dailyWorkList");
		
		setDailyWorkTableRowStyle(0);
	}
}